package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.FlashSaleItemDTO;
import info.batcloud.fanli.core.entity.FlashSaleItem;
import info.batcloud.fanli.core.entity.FlashSaleOrder;
import info.batcloud.fanli.core.entity.User;
import info.batcloud.fanli.core.enums.FlashSaleItemStatus;
import info.batcloud.fanli.core.enums.FlashSaleOrderStatus;
import info.batcloud.fanli.core.exception.BizException;
import info.batcloud.fanli.core.helper.PagingHelper;
import info.batcloud.fanli.core.repository.CommissionItemRepository;
import info.batcloud.fanli.core.repository.FlashSaleItemRepository;
import info.batcloud.fanli.core.repository.FlashSaleOrderRepository;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.service.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class FlashSaleItemServiceImpl implements FlashSaleItemService {

    @Inject
    private FlashSaleItemRepository flashSaleItemRepository;

    @Inject
    private CommissionItemRepository commissionItemRepository;

    @Inject
    private CommissionItemService commissionItemService;

    @Inject
    private FlashSaleRemindService flashSaleRemindService;

    @Inject
    private UserService userService;

    @Inject
    private FlashSaleOrderService flashSaleOrderService;

    @Inject
    private UserRepository userRepository;

    @Inject
    private FlashSaleOrderRepository flashSaleOrderRepository;

    @Override
    public Paging<FlashSaleItemDTO> search(SearchParam param) {
        Specification<FlashSaleItem> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            if (query.getResultType() != Long.class) {
//                root.fetch("item", JoinType.LEFT);
            }
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (StringUtils.isNotBlank(param.getDate())) {
                expressions.add(cb.equal(root.get("date"), param.getDate()));
            }
            if (StringUtils.isNotBlank(param.getSeason())) {
                expressions.add(cb.equal(root.get("season"), param.getSeason()));
            }
            if (param.getFinished() != null) {
                if (param.getFinished()) {
                    expressions.add(cb.equal(root.get("drawnNum"), root.get("totalNum")));
                } else {
                    expressions.add(cb.lessThan(root.get("drawnNum"), root.get("totalNum")));
                }
            }
            if (param.getStatus() != null) {
                expressions.add(cb.equal(root.get("status"), param.getStatus()));
            } else {
                expressions.add(cb.notEqual(root.get("status"), FlashSaleItemStatus.DELETED));
            }
            return predicate;
        };
        Sort sort;
        if (param.getSort() != null) {
            switch (param.getSort()) {
                case ID_DESC:
                    sort = new Sort(Sort.Direction.DESC, "id");
                    break;
                case ID_ASC:
                    sort = new Sort(Sort.Direction.ASC, "id");
                    break;
                default:
                    sort = new Sort(Sort.Direction.DESC, "id");
            }
        } else {
            sort = new Sort(Sort.Direction.DESC, "id");
        }
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<FlashSaleItem> page = flashSaleItemRepository.findAll(specification, pageable);
        Paging<FlashSaleItemDTO> paging = PagingHelper.of(page, item -> toDTO(item, param.getCheckRemindUserId()), param.getPage(), param.getPageSize());
        Map<Long, FlashSaleItemDTO> map = new HashMap<>(20);
        List<Long> flashSaleItemIdList = paging.getResults().stream().map(f -> {
            map.put(f.getId(), f);
            return f.getId();
        }).collect(Collectors.toList());
        if (param.getCheckRemindUserId() != null) {
            List<FlashSaleOrder> orders = flashSaleOrderRepository.findByUserIdAndFlashSaleItemIdIn(param.getCheckRemindUserId(), flashSaleItemIdList);
            orders.forEach(o -> map.get(o.getFlashSaleItem().getId()).setDrawn(true));
        }
        return paging;
    }

    @Override
    public FlashSaleItemDTO findById(Long id) {
        FlashSaleItem one = flashSaleItemRepository.findOne(id);
        FlashSaleItemDTO flashSaleItemDTO = toDT(one);
        return flashSaleItemDTO;
    }

    @Override
    public void updateFlashSaleItem(Long id, FlashSaleItemUpdateParam param) {
        FlashSaleItem item = flashSaleItemRepository.findOne(id);
        BeanUtils.copyProperties(param,item);
        item.setSlogan((param.getSlogan() != null && param.getSlogan().length() > 1) ? param.getSlogan() : "");
        item.setLabel(param.getLabel());
        item.setReturnFee(param.getReturnFee());
        item.setTotalNum(param.getTotalNum());
        flashSaleItemRepository.save(item);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addFlashSaleItem(FlashSaleItemAddParam param) {
        List<FlashSaleItem> list = new ArrayList<>();
        for (int i = 0; i < param.getItemId().length; i++) {
            if (flashSaleItemRepository.countByDateAndSeasonAndItemId(param.getDate(),
                    param.getSeason(), param.getItemId()[i]) > 0) {
                continue;
            }
            FlashSaleItem item = new FlashSaleItem();
            item.setCreateTime(new Date());
            item.setDate(param.getDate());
            item.setSeason(param.getSeason());
            item.setUserLevel(param.getUserLevel()[i]);
            item.setTotalNum(param.getTotalNum()[i]);
            item.setStatus(param.getStatus());
            item.setLabel(param.getLabel()[i]);
            item.setReturnFee(param.getReturnFee()[i]);
            try {
                Date startTime = DateUtils.parseDate(param.getDate() + " " + param.getSeason(), "yyyy-MM-dd HH:mm");
                item.setStartTime(startTime);
            } catch (ParseException e) {
                e.printStackTrace();
            }

            item.setDrawnNum(0);
            item.setSlogan((param.getSlogan() != null && param.getSlogan().length > i + 1) ? param.getSlogan()[i] : "");
            item.setItem(commissionItemRepository.findOne(param.getItemId()[i]));
            item.setStatus(FlashSaleItemStatus.ONSALE);
            list.add(item);
        }
        flashSaleItemRepository.save(list);
    }

    @Override
    public void setStatus(long id, FlashSaleItemStatus status) {
        FlashSaleItem item = flashSaleItemRepository.findOne(id);
        item.setStatus(status);
        flashSaleItemRepository.save(item);
    }

    @Override
    public void setSlogan(long id, String slogan) {
        FlashSaleItem item = flashSaleItemRepository.findOne(id);
        item.setSlogan(slogan);
        flashSaleItemRepository.save(item);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean draw(long id, long userId) {
        FlashSaleItem item = flashSaleItemRepository.findOne(id);
        if (item.getDrawnNum() >= item.getTotalNum()) {
            throw new BizException("当前商品已经抢单一空");
        }
        Date now = new Date();
        String dateStr = item.getDate() + " " + item.getSeason();
        Date startTime;
        try {
            startTime = DateUtils.parseDate(dateStr, "yyyy-MM-dd HH:mm");
        } catch (ParseException e) {
            return false;
        }
        if (now.before(startTime)) {
            throw new BizException("还未开始，请耐心等待");
        }
        if (!userService.isUserLevel(userId, item.getUserLevel())) {
            throw new BizException("对不起，您还不是" + item.getUserLevel().getTitle() + "，无法抢单该商品！");
        }
        // 判断是否已经抢单过
        if (flashSaleOrderRepository.countByUserIdAndFlashSaleItemDateAndFlashSaleItemSeason(userId, item.getDate(), item.getSeason()) > 0) {
            throw new BizException("当前时段已经抢单过一单，不能再次抢单");
        }
        User user = userRepository.findOne(userId);
        FlashSaleOrder fso = new FlashSaleOrder();
        fso.setCreateTime(now);
        fso.setFlashSaleItem(item);
        fso.setFreeFee(item.getReturnFee());
        fso.setStatus(FlashSaleOrderStatus.WAIT_VERIFY);
        fso.setUser(user);
        fso.setItem(item.getItem());
        fso.setUpdateTime(new Date());
        flashSaleOrderRepository.save(fso);
        item.setDrawnNum((item.getDrawnNum() == null ? 0 : item.getDrawnNum()) + 1);
        item.setLastDrawnTime(now);
        flashSaleItemRepository.save(item);
        return true;
    }

    @Override
    public boolean checkDrawn(long id, long userId) {
        FlashSaleOrder order = flashSaleOrderRepository.findByUserIdAndFlashSaleItemId(userId, id);
        return order != null && order.getStatus() == FlashSaleOrderStatus.WAIT_VERIFY;
    }

    private FlashSaleItemDTO toDTO(FlashSaleItem item, Long checkRemindUserId) {
        FlashSaleItemDTO dto = new FlashSaleItemDTO();
        BeanUtils.copyProperties(item, dto);
        dto.setItem(commissionItemService.toCommissionItemDto(item.getItem()));
        try {
            Date date = DateUtils.parseDate(item.getDate(), "yyyy-MM-dd");
            Date now = DateUtils.truncate(new Date(), Calendar.DATE);
             long daytime = 24 * 60 * 60 * 1000;
            int days = Long.valueOf((date.getTime() - now.getTime() + daytime - 1) / daytime).intValue();
            String dayStr;
            if (days < 1) {
                dayStr = "今日";
            } else if (days <= 2) {
                dayStr = "明日";
            } else if (days <= 3) {
                dayStr = "后天";
            } else {
                dayStr = days + "天后";
            }
            dto.setStartTip(dayStr + item.getSeason() + "开抢");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (item.getUserLevel() == null) {
            dto.setUserLevelFit(true);
        }
        if (checkRemindUserId != null) {
            if (item.getUserLevel() != null) {
                dto.setUserLevelFit(userService.isUserLevel(checkRemindUserId, item.getUserLevel()));
            }
            dto.setUserRemind(flashSaleRemindService.checkRemind(item.getId(), checkRemindUserId));
        }
        return dto;
    }

    private FlashSaleItemDTO toDT(FlashSaleItem item) {
        FlashSaleItemDTO dto = new FlashSaleItemDTO();
        BeanUtils.copyProperties(item, dto);
        dto.setItem(commissionItemService.toCommissionItemDto(item.getItem()));
        try {
            Date date = DateUtils.parseDate(item.getDate(), "yyyy-MM-dd");
            Date now = DateUtils.truncate(new Date(), Calendar.DATE);
            long daytime = 24 * 60 * 60 * 1000;
            int days = Long.valueOf((date.getTime() - now.getTime() + daytime - 1) / daytime).intValue();
            String dayStr;
            if (days < 1) {
                dayStr = "今日";
            } else if (days <= 2) {
                dayStr = "明日";
            } else if (days <= 3) {
                dayStr = "后天";
            } else {
                dayStr = days + "天后";
            }
            dto.setStartTip(dayStr + item.getSeason() + "开抢");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (item.getUserLevel() == null) {
            dto.setUserLevelFit(true);
        }
        return dto;
    }
}
